package main

import (
	"eastmoney/zdfb/csv"
	"eastmoney/zdfb/db"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"sort"
	"strconv"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

type FenbuItem map[string]int

type Data struct {
	Qdate int         `json:"qdate"` // qdate 字段是整数类型
	Fenbu []FenbuItem `json:"fenbu"` // fenbu 是一个数组，每个元素是一个 map[string]int
}

// 涨跌分布数据结构
type ZDFenBu struct {
	Data Data `json:"data"`
}

// 涨跌分布详细数据结构
type ZDFenBuDetail struct {
	Zdfb []struct {
		Zdf  string `json:"zdf"`  // 涨跌幅
		Num  int    `json:"num"`  // 数量
		Zb   string `json:"zb"`   // 占比
		Type int    `json:"type"` // 类型
	} `json:"zdfb"`
}

// 涨跌数曲线数据结构
type ZDTCount struct {
	Data struct {
		Zdtcount []struct {
			T   int `json:"t"`   // 时间 (HHMM格式)
			Ztc int `json:"ztc"` // 涨停数
			Dtc int `json:"dtc"` // 跌停数
		} `json:"zdtcount"`
	} `json:"data"`
}

// 封板未遂数据结构
type FBFailed struct {
	Data struct {
		Fbfailed []struct {
			T   int     `json:"t"`   // 时间 (HHMM格式)
			C   int     `json:"c"`   // 封板未遂数
			Zbp float64 `json:"zbp"` // 占比
		} `json:"fbfailed"`
	} `json:"data"`
}

// 涨停池数据结构
type ZTPool struct {
	Data struct {
		Pool []struct {
			Code      string  `json:"c"`      // 股票代码
			Name      string  `json:"n"`      // 股票名称
			Price     float64 `json:"p"`      // 当前价格
			ChangePct float64 `json:"zdp"`    // 涨跌幅
			Amount    int64   `json:"amount"` // 成交额
			Ltsz      float64 `json:"ltsz"`   // 流通市值
			Tshare    float64 `json:"tshare"` // 总市值
			Hs        float64 `json:"hs"`     // 换手率
			Lbc       int     `json:"lbc"`    // 连板数
			Fbt       int     `json:"fbt"`    // 首次封板时间
			Lbt       int     `json:"lbt"`    // 最后封板时间
			Fund      float64 `json:"fund"`   // 封板资金
			Zbc       int     `json:"zbc"`    // 炸板数
			Hybk      string  `json:"hybk"`   // 行业板块
			Zttj      struct {
				Days int `json:"days"` // 连板统计天数
				Ct   int `json:"ct"`   // 连板数
			} `json:"zttj"` // 涨停统计 (可能是对象)
			Market int `json:"m"` // 市场类型
		} `json:"pool"`
	} `json:"data"`
}

type YesPool struct {
	Data struct {
		QDate int `json:"qdate"`
		Pool  []struct {
			Code   string  `json:"c"`      // 股票代码
			Market int     `json:"m"`      // 市场类型
			Name   string  `json:"n"`      // 股票名称
			Price  float64 `json:"p"`      // 当前价格
			Ztp    float64 `json:"ztp"`    // 涨停价
			Zdp    float64 `json:"zdp"`    // 最新价
			Amount float64 `json:"amount"` // 成交额
			Ltsz   float64 `json:"ltsz"`   // 流通市值
			Tshare float64 `json:"tshare"` // 总市值
			Hs     float64 `json:"hs"`     // 换手率
			Zf     float64 `json:"zf"`     // 振幅
			Zs     float64 `json:"zs"`     // 涨速
			Yfbt   int     `json:"yfbt"`   // 昨日封板时间
			Ylbc   int     `json:"ylbc"`   // 昨日连板数
			Hybk   string  `json:"hybk"`   // 行业板块
			Zttj   struct {
				Days int `json:"days"` // 连板统计天数
				Ct   int `json:"ct"`   // 连板数
			} `json:"zttj"`
		} `json:"pool"`
	} `json:"data"`
}

type TopText struct {
	PositionInd int    `json:"PositionInd"`
	Title       string `json:"Title"`
	Content     string `json:"Content"`
}

type TopTextResponse struct {
	Re     bool   `json:"re"`
	Msg    string `json:"message"`
	Result []struct {
		TopText TopText `json:"TopText"`
	} `json:"result"`
}

// 自定义float64类型，用于处理 "float64" 或 "-"
type Customfloat64 float64

// 实现 json.Unmarshaler 接口
func (i *Customfloat64) UnmarshalJSON(data []byte) error {
	// 去除空白字符
	s := string(data)

	if s == `"-"` || s == "-" || s == `""` || s == "" {
		*i = 0.0 // 将 "-" 转换为 0
		return nil
	}

	// 否则尝试转成整数
	v, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return err
	}
	*i = Customfloat64(v)
	return nil
}

type GnTop struct {
	MarketCode         int           `json:"f1"`   // 市场代码（1: 沪市, 2: 深市, 8: 创业板, 61: 科创板）？
	BoardCode          string        `json:"f12"`  // 板块代码（如 BK 开头为东财板块编码）
	BoardType          int           `json:"f13"`  // 板块类型（90: 概念, 91: 行业, 92: 地域）
	BoardName          string        `json:"f14"`  // 板块名称
	Price              Customfloat64 `json:"f2"`   // price / 100.0
	ChangePercent      Customfloat64 `json:"f3"`   // 涨跌幅 / 100.0
	ChangeAmount       Customfloat64 `json:"f4"`   // 涨跌额 / 100.0
	TurnoverRate       Customfloat64 `json:"f8"`   // 换手率 / 100.0
	TotalMarketValue   int64         `json:"f20"`  // 总市值
	RisingCount        int           `json:"f104"` // 上涨家数
	FallingCount       int           `json:"f105"` // 下跌家数
	TopStockName       string        `json:"f128"` // 领涨股名称
	TopStockRise       Customfloat64 `json:"f136"` // 领涨股涨幅 / 100.0
	TopStockCode       string        `json:"f140"` // 领涨股代码
	F141               int           `json:"f141"` // ?领涨股交所(0-深，1-沪)
	DataSource         int           `json:"f152"` // 数据来源（0-默认板块，1-自定义板块，2-概念板块）
	LowestStockName    string        `json:"f207"` // 最低股票名称
	LowestStockCode    string        `json:"f208"` // 最低股票代码
	F209               int           `json:"f209"` // ?最低股交所(0-深，1-沪)
	LowestStockPercent float64       `json:"f222"` // 最低股票涨幅 / 100.0
}

type GnTopResponse struct {
	Data struct {
		Total int     `json:"total"`
		GnTop []GnTop `json:"diff"`
	} `json:"data"`
}

type GnStock struct {
	MarketCode    int           `json:"f1"`  // 市场代码（1: 沪市, 2: 深市, 8: 创业板, 61: 科创板）？
	Code          string        `json:"f12"` // 代码（如 BK 开头为东财板块编码）
	Name          string        `json:"f14"` // 名称
	Martket       int           `json:"f13"` // 交易所
	Price         Customfloat64 `json:"f2"`  // price / 100.0
	ChangePercent Customfloat64 `json:"f3"`  // 涨跌幅 / 100.0
	BoardCode     string
}

type GnStockListResponse struct {
	Data struct {
		Total   int       `json:"total"`
		GnStock []GnStock `json:"diff"`
	} `json:"data"`
}

type SaveType string

func (s SaveType) Equets() bool {
	str := string(s)
	if str != string(DB) && str != string(CSV) {
		return true
	} else {
		return false
	}
}

const CSV SaveType = "csv"
const DB SaveType = "db"

func main() {

	var formatStr string
	var dbPath string

	flag.StringVar(&formatStr, "format", string(DB), "-format csv|db")
	flag.StringVar(&dbPath, "file", string(DB), "-d sqlite3 data file.")
	flag.Parse()

	var saveFlag SaveType = SaveType(formatStr)

	if saveFlag != CSV && saveFlag != DB {
		flag.Usage()
		return
	}

	if saveFlag == DB {
		db.InitDB(dbPath)
	}

	currentDate := time.Now().Format("20060102")

	// 1. 获取涨跌分布数据
	currentDate = getZdFbAndSave(saveFlag, currentDate)

	// // 2. 获取涨跌数曲线数据
	// getZdtCountAndSave(saveFlag, currentDate)

	// // 3. 获取封板未遂曲线数据
	// getFbFailedAndSave(saveFlag, currentDate)

	// // 4. 获取涨停池数据
	// getZtPoolAndSave(currentDate, saveFlag)

	// //5. 获取昨日涨停股票池
	// getYesPoolAndSave(currentDate, saveFlag)

	//6、投资评分
	getTopText(currentDate, saveFlag)

	//7、概念板块Top（轮动）
	getGnTopAndSave(saveFlag, currentDate)

}

func getGnTopAndSave(saveFlag SaveType, currentDate string) {
	//baseURL := "https://91.push2.eastmoney.com/api/qt/clist/get"
	baseURL := "https://push2.eastmoney.com/api/qt/clist/get"
	u, _ := url.Parse(baseURL)

	//
	Top10Params := url.Values{}
	Top10Params.Add("np", "1")
	Top10Params.Add("fltt", "1")
	Top10Params.Add("invt", "2")
	Top10Params.Add("cb", "jQuery37107928035824233592_1750855556845")
	Top10Params.Add("fs", "m:90+t:3+f:!50")
	Top10Params.Add("fields", "f12,f13,f14,f1,f2,f4,f3,f152,f20,f8,f104,f105,f128,f140,f141,f207,f208,f209,f136,f222")
	Top10Params.Add("fid", "f3")
	Top10Params.Add("pn", "1")
	Top10Params.Add("pz", "100")
	Top10Params.Add("po", "1")
	Top10Params.Add("dect", "1")
	Top10Params.Add("ut", "fa5fd1943c7b386f172d6893dbfba10b")
	Top10Params.Add("wbp2u", "|0|0|0|web")
	Top10Params.Add("_", strconv.Itoa(int(time.Now().UnixMilli())))

	GnStockListParams := url.Values{}
	GnStockListParams.Add("cb", "jQuery11230594630921823553_1750964491489")
	GnStockListParams.Add("fid", "f3")
	GnStockListParams.Add("po", "1")
	GnStockListParams.Add("pz", "50")
	GnStockListParams.Add("pn", "1")
	GnStockListParams.Add("np", "1")
	GnStockListParams.Add("fltt", "2")
	GnStockListParams.Add("invt", "2")
	GnStockListParams.Add("ut", "8dec03ba335b81bf4ebdf7b29ec27d15")
	// GnStockListParams.Set("fs", "b:BK0815")
	GnStockListParams.Add("fields", "f12,f14,f2,f3,f62,f184,f66,f69,f72,f75,f78,f81,f84,f87,f204,f205,f124,f1,f13")

	u.RawQuery = Top10Params.Encode()
	gnTopData, respBody, err := fetchData(u.String())
	if err != nil {
		fmt.Printf("数据获取失败: %v\n", err)
	} else {
		var gnTopResponse GnTopResponse
		if err := json.Unmarshal(gnTopData, &gnTopResponse); err != nil {
			fmt.Printf("概念数据解析失败: %v\n", err)
			fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
		} else {
			var top10 []GnTop
			// var BK0815 GnTop //BK0815 昨日涨停
			// var BK1050 GnTop //BK1050 昨日涨停_含一字
			// var BK0816 GnTop //BK0816 昨日连板
			// var BK1051 GnTop //BK1051 昨日连板_含一字

			for _, v := range gnTopResponse.Data.GnTop {

				switch v.BoardCode {
				case "BK0815":
					// BK0815 = v
					continue
				case "BK1050":
					// BK1050 = v
					continue
				case "BK0816":
					// BK0816 = v
					continue
				case "BK1051":
					// BK1051 = v
					continue
				default:
					if len(top10) < 10 {
						top10 = append(top10, v)
					}
				}
			}

			// top10 = append(top10, BK0815, BK1050, BK0816, BK1051)

			//save
			switch saveFlag {
			case CSV:
				//saveCsv
				var lines []string
				for _, v := range top10 {
					arg := []interface{}{
						currentDate, v.MarketCode, v.BoardCode, v.BoardType, v.BoardName, //
						v.Price / 100.0, v.ChangePercent / 100.0, v.ChangeAmount / 100.0, v.TurnoverRate / 100.0, float64(v.TotalMarketValue) / 100000000.0,
						v.RisingCount, v.FallingCount, v.TopStockName, v.TopStockRise / 100.0, v.TopStockCode, //
						v.F141, v.DataSource, v.LowestStockName, v.LowestStockCode, v.F209, //
						v.LowestStockPercent / 100.0,
					}
					formatStr := "%v,%v,%v,%v,%v,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d,%s,%.2f,%s,%d,%d,%s,%s,%d,%.2f"
					lines = append(lines, fmt.Sprintf(formatStr, arg...))
				}
				fmt.Println(len(lines))
				gntop10File := fmt.Sprintf("gntop10_%s.csv", currentDate)
				fmt.Println("csv file:", gntop10File)
				headLine := "qdate, 市场代码, 板块代码, 板块类型, 板块名称, 最新价格, 涨跌幅,涨跌额,换手率,总市值(亿),上涨家数,下跌家数,领涨股名称,领涨股涨幅,领涨股代码,领涨股交所(0-深，1-沪),数据来源,最低股票名称,最低股票代码,最低股交所(0-深，1-沪),最低股票涨幅"
				csv.Save(gntop10File, headLine, lines)

			case DB:

				//saveDB
				var values [][]interface{}
				for _, v := range top10 {
					row := []interface{}{
						currentDate, v.MarketCode, v.BoardCode, v.BoardType, v.BoardName, //
						v.Price / 100.0, v.ChangePercent / 100.0, v.ChangeAmount / 100.0, v.TurnoverRate / 100.0, v.TotalMarketValue,
						v.RisingCount, v.FallingCount, v.TopStockName, v.TopStockRise / 100.0, v.TopStockCode, //
						v.F141, v.DataSource, v.LowestStockName, v.LowestStockCode, v.F209, //
						v.LowestStockPercent / 100.0,
					}
					values = append(values, row)
				}
				db.SaveDB(values, db.GnTop10TableName, db.GnTopColumns, db.GnTopUpdateColumns)
			}

			//stocks
			var gnstocks [][]interface{}
			for _, v := range top10 {
				GnStockListParams.Set("fs", fmt.Sprintf("b:%s", v.BoardCode))
				u.RawQuery = GnStockListParams.Encode()
				respBody, _, err := fetchData(u.String())
				if err != nil {
					fmt.Println("error:", u.String(), err)
				} else {
					var gnStockListResponse GnStockListResponse
					if err := json.Unmarshal(respBody, &gnStockListResponse); err != nil {
						fmt.Printf("概念股票数据解析失败: %v\n", err)
						fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
					} else {

						for _, w := range gnStockListResponse.Data.GnStock {
							//	"qdate", "market_code", "code", "name", "market", //
							//"price", "change_percent", "board_code",
							values := []interface{}{currentDate, w.MarketCode, w.Code, w.Name, w.Martket, //
								w.Price, w.ChangePercent, v.BoardCode, v.BoardName}
							gnstocks = append(gnstocks, values)
						}
					}
				}
			}

			//save
			switch saveFlag {
			case CSV:
				//saveCsv
				var lines []string
				for _, v := range gnstocks {
					//	"qdate", "market_code", "code", "name", "market", //
					//"price", "change_percent", "board_code",
					formatStr := "%v,%v,%v,%v,%v,%0.2f,%0.2f,%v,%v"
					lines = append(lines, fmt.Sprintf(formatStr, v...))
				}
				fmt.Println(len(lines))
				gnstockFile := fmt.Sprintf("gnstock_%s.csv", currentDate)
				fmt.Println("csv file:", gnstockFile)
				headLine := "qdate, 市场代码, 股票代码, 股票名称, market, 股价, 涨跌额, 版块代码, 版块名称"
				csv.Save(gnstockFile, headLine, lines)

			case DB:
				//saveDB
				db.SaveDB(gnstocks, db.GnStockTable, db.GnStockColumns, db.GnStockUpdateColumns)
			}

		}
	}
}

// 6、投资评分
func getTopText(currentDate string, saveFlag SaveType) {

	topTextData, respBody, err := fetchData("https://quote.eastmoney.com/ztb/api/cangweiinfos")
	if err != nil {
		fmt.Printf("投资评分数据获取失败: %v\n", err)
	} else {
		var topTextResponse TopTextResponse
		if err := json.Unmarshal(topTextData, &topTextResponse); err != nil {
			fmt.Printf("投资评分数据解析失败: %v\n", err)
			fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
		} else {
			topText := topTextResponse.Result[0].TopText
			fmt.Printf("6. 投资评分: \n%d - %s - %s\n", topText.PositionInd, topText.Title, topText.Content)
			if saveFlag == DB {
				values := [][]interface{}{{currentDate, topText.PositionInd, topText.Title, topText.Content}}
				db.SaveDB(values, db.PositionIndTable, db.PositionIndColumns, db.PositionIndUpdateColumns)
			}

		}
	}
}

// 获取昨日涨停股票池
func getYesPoolAndSave(currentDate string, saveFlag SaveType) {
	fmt.Println("5. 获取昨日涨停股票池")
	yesPoolURL := fmt.Sprintf("https://push2ex.eastmoney.com/getYesterdayZTPool?cb=callbackdata4046752&ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wz.ztzt&Pageindex=0&pagesize=6000&sort=zs%%3Adesc&date=%s&_=%d", currentDate, time.Now().UnixMilli())
	yesPoolData, respBody, err := fetchData(yesPoolURL)

	if err != nil {
		fmt.Printf("昨日涨停池数据获取失败: %v\n", err)
	} else {
		var yesPool YesPool
		if err := json.Unmarshal(yesPoolData, &yesPool); err != nil {
			fmt.Printf("昨日涨停池数据解析失败: %v\n", err)
			fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
		} else {

			//save
			switch saveFlag {
			case CSV:
				//saveCsv
				var lines []string
				for _, v := range yesPool.Data.Pool {
					lines = append(lines, fmt.Sprintf("%s,%s,%s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d,%s,%d,%d,%d", currentDate, v.Code, v.Name, v.Price/100.0, v.Zf, v.Amount, v.Ltsz, v.Tshare, v.Hs, v.Yfbt, v.Ylbc, v.Hybk, v.Zttj.Days, v.Zttj.Ct, v.Market))
				}

				yesPoolFile := fmt.Sprintf("yesPool_%s.csv", currentDate)
				fmt.Println("csv file:", yesPoolFile)
				csv.Save(yesPoolFile, "qdate, code, name, price, zf, amount, ltsz(流通市值), tshare(总市值), hs(换手率), Yfbt(昨日封板时间), Ylbc(昨日莲板数), hybk(行业), zttj_days(涨停统计天数), zttj_ct(涨停数), market(市场)", lines)

			case DB:

				//saveDB
				var values [][]interface{}
				for _, v := range yesPool.Data.Pool {
					row := []interface{}{
						currentDate, v.Code, v.Name, v.Price / 100.0, v.Zf, v.Amount, v.Ltsz, v.Tshare, v.Hs, v.Yfbt, v.Ylbc, v.Hybk, v.Zttj.Days, v.Zttj.Ct, v.Market,
					}
					values = append(values, row)
				}
				db.SaveDB(values, db.YesPoolTableName, db.YesPoolColumns, db.YesPoolUpdateColumns)
			}

		}
	}
}

// 获取涨停池数据
func getZtPoolAndSave(currentDate string, saveFlag SaveType) {
	fmt.Println("4. 获取涨停池数据")
	ztPoolURL := fmt.Sprintf("https://push2ex.eastmoney.com/getTopicZTPool?cb=callback&ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wz.ztzt&Pageindex=0&pagesize=6000&sort=fbt:asc&date=%s&_=%d", currentDate, time.Now().UnixMilli())
	ztPoolData, respBody, err := fetchData(ztPoolURL)
	if err != nil {
		fmt.Printf("涨停池数据获取失败: %v\n", err)
	} else {
		var ztPool ZTPool
		if err := json.Unmarshal(ztPoolData, &ztPool); err != nil {
			fmt.Printf("涨停池数据解析失败: %v\n", err)
			fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
		} else {

			//save
			switch saveFlag {
			case CSV:
				//saveCsv
				var lines []string
				for _, v := range ztPool.Data.Pool {
					lines = append(lines, fmt.Sprintf("%s,%s,%s,%.2f,%.2f,%d,%.2f,%.2f,%.2f,%d,%d,%d,%.2f,%d,%s,%d,%d,%d", currentDate, v.Code, v.Name, v.Price/100.0, v.ChangePct, v.Amount, v.Ltsz, v.Tshare, v.Hs, v.Lbc, v.Fbt, v.Lbt, v.Fund, v.Zbc, v.Hybk, v.Zttj.Days, v.Zttj.Ct, v.Market))
				}

				ztPoolFile := fmt.Sprintf("ztPool_%s.csv", currentDate)
				fmt.Println("csv file:", ztPoolFile)
				csv.Save(ztPoolFile, "qdate, code, name, price, change_pct, amount, ltsz(流通市值), tshare(总市值), hs(换手率), lbc(连板数), fbt(首次封板时间), lbt(最近封板时间), fund(封板资金), zbc(炸板数), hybk(行业), zttj_days(涨停统计天数), zttj_ct(涨停数), market(市场)", lines)

			case DB:

				//saveDB
				var values [][]interface{}
				for _, v := range ztPool.Data.Pool {
					row := []interface{}{
						currentDate, v.Code, v.Name, v.Price / 100.0, v.ChangePct, v.Amount, v.Ltsz, v.Tshare, v.Hs, v.Lbc, v.Fbt, v.Lbt, v.Fund, v.Zbc, v.Hybk, v.Zttj.Days, v.Zttj.Ct, v.Market,
					}
					values = append(values, row)
				}
				db.SaveDB(values, db.ZtPoolTableName, db.ZtPoolColumns, db.ZtPoolUpdateColumns)
			}
		}
	}
}

// 3. 获取封板未遂曲线数据
func getFbFailedAndSave(saveFlag SaveType, currentDate string) {
	fmt.Println("3. 获取封板未遂曲线数据")
	fbFailedURL := fmt.Sprintf("https://push2ex.eastmoney.com/getTopicFBFailed?cb=callback&ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wz.ztzt&time=0&_=%d", time.Now().UnixMilli())
	fbFailedData, respBody, err := fetchData(fbFailedURL)
	if err != nil {
		fmt.Printf("封板未遂曲线数据获取失败: %v\n", err)
	} else {
		var fbFailed FBFailed
		if err := json.Unmarshal(fbFailedData, &fbFailed); err != nil {
			fmt.Printf("封板未遂曲线数据解析失败: %v\n", err)
			fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
		} else {

			switch saveFlag {
			case CSV:
				//saveCsv
				var lines []string
				for _, v := range fbFailed.Data.Fbfailed {
					lines = append(lines, fmt.Sprintf("%s,%d,%d,%.2f", currentDate, v.T, v.C, v.Zbp))
				}
				fbFailedFile := fmt.Sprintf("fbFailed_%s.csv", currentDate)
				fmt.Println("csv file:", fbFailedFile)
				csv.Save(fbFailedFile, "qdate,t,c,zbp", lines)
			case DB:
				//saveDB
				var values [][]interface{}
				for _, v := range fbFailed.Data.Fbfailed {
					row := []interface{}{
						currentDate, v.T, v.C, v.Zbp,
					}
					values = append(values, row)
				}
				db.SaveDB(values, db.FbFailedTableName, db.FbFailedTColumns, db.FbFailedTUpdateColumns)
			}
		}
	}
}

// 2. 获取涨跌数曲线数据
func getZdtCountAndSave(saveFlag SaveType, currentDate string) {
	fmt.Println("2. 获取涨跌数曲线数据")
	zdtCountURL := fmt.Sprintf("https://push2ex.eastmoney.com/getTopicZDTCount?cb=callback&ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wz.ztzt&time=0&_=%d", time.Now().UnixMilli())
	zdtCountData, respBody, err := fetchData(zdtCountURL)
	if err != nil {
		fmt.Printf("涨跌数曲线数据获取失败: %v\n", err)
	} else {
		var zdtCount ZDTCount
		if err := json.Unmarshal(zdtCountData, &zdtCount); err != nil {
			fmt.Printf("涨跌数曲线数据解析失败: %v\n", err)
			fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
		} else {

			//save
			switch saveFlag {
			case CSV:
				//saveCsv
				var lines []string
				for _, v := range zdtCount.Data.Zdtcount {
					lines = append(lines, fmt.Sprintf("%s,%d,%d,%d", currentDate, v.T, v.Ztc, v.Dtc))
				}
				zdtCountFile := fmt.Sprintf("zdtCount_%s.csv", currentDate)
				fmt.Println("csv file:", zdtCountFile)
				csv.Save(zdtCountFile, "qdate,t,ztc,dtc", lines)
			case DB:
				//saveDB
				var values [][]interface{}
				for _, v := range zdtCount.Data.Zdtcount {
					row := []interface{}{
						currentDate, v.T, v.Ztc, v.Dtc,
					}
					values = append(values, row)
				}
				db.SaveDB(values, db.ZdtcountTableName, db.ZdtcountColumns, db.ZdtcountUpdateColumns)
			}
		}
	}
}

// 1. 获取涨跌分布数据
func getZdFbAndSave(saveFlag SaveType, currentDate string) string {
	fmt.Println("1. 获取涨跌分布数据")
	zdfbURL := fmt.Sprintf("https://push2ex.eastmoney.com/getTopicZDFenBu?cb=callback&ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wz.ztzt&_=%d", time.Now().UnixMilli())
	zdfbData, respBody, err := fetchData(zdfbURL)
	if err != nil {
		fmt.Printf("涨跌分布数据获取失败: %v\n", err)
	} else {
		var zdfb ZDFenBu
		if err := json.Unmarshal(zdfbData, &zdfb); err != nil {
			fmt.Printf("涨跌分布数据解析失败: %v\n", err)
			fmt.Printf("原始响应前200字符: %s\n", string(respBody)[:200])
		} else {

			currentDate = strconv.Itoa(zdfb.Data.Qdate)

			// 创建一个 slice 来保存所有 key-value 对
			var pairs []struct {
				KeyStr string
				KeyInt int
				Value  int
			}

			// 遍历并解析 key 为整数
			for _, item := range zdfb.Data.Fenbu {
				for k, v := range item {
					if num, err := strconv.Atoi(k); err == nil {
						pairs = append(pairs, struct {
							KeyStr string
							KeyInt int
							Value  int
						}{k, num, v})
					}
				}
			}
			// 按照整数大小排序
			sort.Slice(pairs, func(i, j int) bool {
				return pairs[i].KeyInt < pairs[j].KeyInt
			})

			//save
			switch saveFlag {
			case CSV:
				//saveCsv
				var lines []string
				for _, v := range pairs {
					lines = append(lines, fmt.Sprintf("%s,%d,%d", currentDate, v.KeyInt, v.Value))
				}

				fenbuFile := fmt.Sprintf("fenbu_%s.csv", currentDate)
				fmt.Println("csv file:", fenbuFile)
				csv.Save(fenbuFile, "qdate,key,value", lines)
			case DB:
				//saveDB
				var values [][]interface{}
				for _, v := range pairs {
					row := []interface{}{
						currentDate, v.KeyInt, v.Value,
					}
					values = append(values, row)
				}
				db.SaveDB(values, db.ZdfenbuTableName, db.ZdfenbuColumns, db.ZdfenbuUpdateColumns)
			}

		}
	}
	return currentDate
}

// fetchData 获取数据并处理JSONP响应
func fetchData(url string) ([]byte, []byte, error) {
	referer := "https://quote.eastmoney.com/ztb/"

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fmt.Println("创建请求失败:", url, nil)
		return nil, nil, err
	}
	req.Header.Add("Referer", referer)
	req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
	resp, err := client.Do(req)
	// fmt.Println(url)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	// 处理JSONP响应：提取JSON部分
	re := regexp.MustCompile(`\w+\(([\s\S]*)\);?$`)
	matches := re.FindStringSubmatch(string(body))

	var jsonData string
	if len(matches) > 1 {
		jsonData = matches[1]
	} else {
		jsonData = string(body)
	}
	// fmt.Println(string(jsonData))

	return []byte(jsonData), body, nil
}
